package eva

import (
	"testing"

	"gitcode.com/deyiyangyang/gascheme/ast"
	"gitcode.com/deyiyangyang/gascheme/obj"
	"gitcode.com/deyiyangyang/gascheme/tok"
)

// TestConsFunction tests the cons function
func TestConsFunction(t *testing.T) {
	// Test (cons 1 ())
	consExpr1 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "cons"},
			&ast.IntegerLiteral{Value: 1},
			&ast.ListExpression{Elements: []ast.Expression{}},
		},
	}

	// Test (cons 1 2) - cons with non-list as second argument
	consExpr2 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "cons"},
			&ast.IntegerLiteral{Value: 1},
			&ast.IntegerLiteral{Value: 2},
		},
	}

	// Test (cons 1 (cons 2 (cons 3 ())))
	consExpr3 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "cons"},
			&ast.IntegerLiteral{Value: 1},
			&ast.ListExpression{
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "cons"},
					&ast.IntegerLiteral{Value: 2},
					&ast.ListExpression{
						Elements: []ast.Expression{
							&ast.SymbolLiteral{Value: "cons"},
							&ast.IntegerLiteral{Value: 3},
							&ast.ListExpression{Elements: []ast.Expression{}},
						},
					},
				},
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test case 1: (cons 1 ())
	result1 := Eval(consExpr1, env)
	list1, ok := result1.(*obj.List)
	if !ok {
		t.Errorf("cons didn't return a List. Got %T", result1)
		return
	}

	if list1.Length() != 1 {
		t.Errorf("cons returned a list with wrong length. Got %d, want 1", list1.Length())
		return
	}

	elements1 := list1.ToSlice()
	if intVal, ok := elements1[0].(*obj.Integer); !ok || intVal.Value != 1 {
		t.Errorf("cons returned wrong element. Got %v, want 1", elements1[0])
	}

	// Test case 2: (cons 1 2)
	result2 := Eval(consExpr2, env)
	list2, ok := result2.(*obj.List)
	if !ok {
		t.Errorf("cons didn't return a List. Got %T", result2)
		return
	}

	if list2.Length() != 2 {
		t.Errorf("cons returned a list with wrong length. Got %d, want 2", list2.Length())
		return
	}
	
	elements2 := list2.ToSlice()
	if intVal, ok := elements2[0].(*obj.Integer); !ok || intVal.Value != 1 {
		t.Errorf("cons returned wrong first element. Got %v, want 1", elements2[0])
	}
	if intVal, ok := elements2[1].(*obj.Integer); !ok || intVal.Value != 2 {
		t.Errorf("cons returned wrong second element. Got %v, want 2", elements2[1])
	}

	// Test case 3: (cons 1 (cons 2 (cons 3 ())))
    result3 := Eval(consExpr3, env)
    list3, ok := result3.(*obj.List)
    if !ok {
        t.Errorf("cons didn't return a List. Got %T", result3)
        return
    }

    // With proper cons implementation, (cons 1 (cons 2 (cons 3 ()))) 
    // should produce (1 2 3) which is a list with 3 elements
    elements3 := list3.ToSlice()
    if len(elements3) != 3 {
        t.Errorf("cons chain returned a list with wrong length. Got %d, want 3", len(elements3))
        return
    }
    if len(elements3) != 3 {
        t.Errorf("cons chain returned a list with wrong length. Got %d, want 3", len(elements3))
        return
    }

    // Verify the elements form a proper flat structure
    if intVal, ok := elements3[0].(*obj.Integer); !ok || intVal.Value != 1 {
        t.Errorf("First element is wrong. Got %v, want 1", elements3[0])
    }
    
    if intVal, ok := elements3[1].(*obj.Integer); !ok || intVal.Value != 2 {
        t.Errorf("Second element is wrong. Got %v, want 2", elements3[1])
    }
    
    if intVal, ok := elements3[2].(*obj.Integer); !ok || intVal.Value != 3 {
        t.Errorf("Third element is wrong. Got %v, want 3", elements3[2])
    }
}

// TestConsWithQuotedList tests the specific case from the issue: (cons 'a '(b))
func TestConsWithQuotedList(t *testing.T) {
	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Manually construct '(b) which should be a list with one element 'b'
	quotedB := &obj.List{Car: &obj.Symbol{Value: "b"}, Cdr: &obj.List{}}

	// Test what (cons 'a '(b)) produces
	// 'a evaluates to the symbol a
	symA := &obj.Symbol{Value: "a"}

	// Call cons directly
	result := Cons([]obj.Object{symA, quotedB}, env)

	list, ok := result.(*obj.List)
	if !ok {
		t.Errorf("cons didn't return a List. Got %T", result)
		return
	}

	elements := list.ToSlice()
	// Should have 2 elements: a and b (not a and (b))
	if len(elements) != 2 {
		t.Errorf("cons returned a list with wrong length. Got %d, want 2", len(elements))
		return
	}

	// First element should be symbol 'a'
	if sym, ok := elements[0].(*obj.Symbol); !ok || sym.Value != "a" {
		t.Errorf("First element is wrong. Got %v, want symbol 'a'", elements[0])
	}

	// Second element should be symbol 'b' (not a list containing 'b')
	if sym, ok := elements[1].(*obj.Symbol); !ok || sym.Value != "b" {
		t.Errorf("Second element is wrong. Got %v, want symbol 'b'", elements[1])
	}
}

// TestDottedPair tests the dotted pair functionality: (cons 'a 'b) should produce (a . b)
func TestDottedPair(t *testing.T) {
	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Manually construct the objects for 'a and 'b
	symA := &obj.Symbol{Value: "a"}
	symB := &obj.Symbol{Value: "b"}

	// Test what (cons 'a 'b) produces
	result := Cons([]obj.Object{symA, symB}, env)

	list, ok := result.(*obj.List)
	if !ok {
		t.Errorf("cons didn't return a List. Got %T", result)
		return
	}

	elements := list.ToSlice()
	// Should have 2 elements: a and b
	if len(elements) != 2 {
		t.Errorf("cons returned a list with wrong length. Got %d, want 2", len(elements))
		return
	}

	// First element should be symbol 'a'
	if sym, ok := elements[0].(*obj.Symbol); !ok || sym.Value != "a" {
		t.Errorf("First element is wrong. Got %v, want symbol 'a'", elements[0])
	}

	// Second element should be symbol 'b'
	if sym, ok := elements[1].(*obj.Symbol); !ok || sym.Value != "b" {
		t.Errorf("Second element is wrong. Got %v, want symbol 'b'", elements[1])
	}

	// Check that it's displayed as a dotted pair
	expected := "(a . b)"
	actual := list.Inspect()
	if actual != expected {
		t.Errorf("Dotted pair display is wrong. Got %s, want %s", actual, expected)
	}
}

// TestProperListVsDottedPair tests that proper lists and dotted pairs are displayed correctly
func TestProperListVsDottedPair(t *testing.T) {
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test case 1: Dotted pair (cons 'a 'b) should display as (a . b)
	symA := &obj.Symbol{Value: "a"}
	symB := &obj.Symbol{Value: "b"}
	dottedPair := Cons([]obj.Object{symA, symB}, env)
	
	if dottedPair.Inspect() != "(a . b)" {
		t.Errorf("Dotted pair (cons 'a 'b) displayed incorrectly. Got %s, want (a . b)", dottedPair.Inspect())
	}

	// Test case 2: Proper list (cons 'a '(b)) should display as (a b)
	quotedB := &obj.List{Car: &obj.Symbol{Value: "b"}, Cdr: &obj.List{}}
	properList := Cons([]obj.Object{symA, quotedB}, env)
	
	// After flattening, this becomes a list with elements [a, b] where b is a symbol
	// According to our current implementation, this is displayed as (a b) 
	// because it's a proper list created by consing to a proper list
	if properList.Inspect() != "(a b)" {
		t.Errorf("Proper list (cons 'a '(b)) displayed incorrectly. Got %s, want (a b)", properList.Inspect())
	}

	// Test case 3: Complex proper list (cons 'a '(b c d)) should display as (a b c d)
	quotedBCD := &obj.List{
		Car: &obj.Symbol{Value: "b"},
		Cdr: &obj.List{
			Car: &obj.Symbol{Value: "c"},
			Cdr: &obj.List{
				Car: &obj.Symbol{Value: "d"},
				Cdr: &obj.List{},
			},
		},
	}
	complexProperList := Cons([]obj.Object{symA, quotedBCD}, env)
	
	if complexProperList.Inspect() != "(a b c d)" {
		t.Errorf("Complex proper list displayed incorrectly. Got %s, want (a b c d)", complexProperList.Inspect())
	}

	// Test case 4: Nested structure
	// (cons 'a (cons 'b 'c)) creates a structure where:
	// 1. (cons 'b 'c) produces (b . c) which is a dotted pair
	// 2. (cons 'a (b . c)) produces (a b . c) which is an improper list
	innerDottedPair := Cons([]obj.Object{&obj.Symbol{Value: "b"}, &obj.Symbol{Value: "c"}}, env)
	nestedStructure := Cons([]obj.Object{&obj.Symbol{Value: "a"}, innerDottedPair}, env)
	
	if nestedStructure.Inspect() != "(a b . c)" {
		t.Errorf("Nested structure displayed incorrectly. Got %s, want (a b . c)", nestedStructure.Inspect())
	}
}

// TestEmptyList tests that empty lists are displayed correctly
func TestEmptyList(t *testing.T) {
	emptyList := &obj.List{}
	
	if emptyList.Inspect() != "()" {
		t.Errorf("Empty list displayed incorrectly. Got %s, want ()", emptyList.Inspect())
	}
}

// TestListWithMoreThanTwoElements tests that lists with more than 2 elements are displayed correctly
func TestListWithMoreThanTwoElements(t *testing.T) {
	// Test (a b c)
	listABC := &obj.List{
		Car: &obj.Symbol{Value: "a"},
		Cdr: &obj.List{
			Car: &obj.Symbol{Value: "b"},
			Cdr: &obj.List{
				Car: &obj.Symbol{Value: "c"},
				Cdr: &obj.List{},
			},
		},
	}
	
	if listABC.Inspect() != "(a b c)" {
		t.Errorf("List with 3 elements displayed incorrectly. Got %s, want (a b c)", listABC.Inspect())
	}

	// Test (1 2 3 4)
	list1234 := &obj.List{
		Car: &obj.Integer{Value: 1},
		Cdr: &obj.List{
			Car: &obj.Integer{Value: 2},
			Cdr: &obj.List{
				Car: &obj.Integer{Value: 3},
				Cdr: &obj.List{
					Car: &obj.Integer{Value: 4},
					Cdr: &obj.List{},
				},
			},
		},
	}
	
	if list1234.Inspect() != "(1 2 3 4)" {
		t.Errorf("List with 4 elements displayed incorrectly. Got %s, want (1 2 3 4)", list1234.Inspect())
	}
}

// TestCarFunction tests the car function
func TestCarFunction(t *testing.T) {
	// Test (car (list 1 2 3))
	carExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "car"},
			&ast.ListExpression{
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "list"},
					&ast.IntegerLiteral{Value: 1},
					&ast.IntegerLiteral{Value: 2},
					&ast.IntegerLiteral{Value: 3},
				},
			},
		},
	}

	// Test (car ()) - should return an error
	carEmptyExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "car"},
			&ast.ListExpression{Elements: []ast.Expression{}},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test case 1: (car (list 1 2 3))
	result := Eval(carExpr, env)
	intVal, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("car didn't return an Integer. Got %T", result)
		return
	}

	if intVal.Value != 1 {
		t.Errorf("car returned wrong value. Got %d, want 1", intVal.Value)
	}

	// Test case 2: (car ())
	errorResult := Eval(carEmptyExpr, env)
	errorObj, ok := errorResult.(*obj.Error)
	if !ok {
		t.Errorf("car with empty list didn't return an error. Got %T", errorResult)
		return
	}

	if errorObj.Message != "car: cannot apply to empty list" {
		t.Errorf("car with empty list returned wrong error message. Got %s", errorObj.Message)
	}
}

// TestCdrFunction tests the cdr function
func TestCdrFunction(t *testing.T) {
	// Test (cdr (list 1 2 3))
	cdrExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "cdr"},
			&ast.ListExpression{
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "list"},
					&ast.IntegerLiteral{Value: 1},
					&ast.IntegerLiteral{Value: 2},
					&ast.IntegerLiteral{Value: 3},
				},
			},
		},
	}

	// Test (cdr (list 1))
	cdrSingleExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "cdr"},
			&ast.ListExpression{
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "list"},
					&ast.IntegerLiteral{Value: 1},
				},
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test case 1: (cdr (list 1 2 3))
	result1 := Eval(cdrExpr, env)
	list1, ok := result1.(*obj.List)
	if !ok {
		t.Errorf("cdr didn't return a List. Got %T", result1)
		return
	}

	elements1 := list1.ToSlice()
	if len(elements1) != 2 {
		t.Errorf("cdr returned a list with wrong length. Got %d, want 2", len(elements1))
		return
	}

	if intVal, ok := elements1[0].(*obj.Integer); !ok || intVal.Value != 2 {
		t.Errorf("First element of cdr result is wrong. Got %v, want 2", elements1[0])
	}

	if intVal, ok := elements1[1].(*obj.Integer); !ok || intVal.Value != 3 {
		t.Errorf("Second element of cdr result is wrong. Got %v, want 3", elements1[1])
	}

	// Test case 2: (cdr (list 1))
	result2 := Eval(cdrSingleExpr, env)
	list2, ok := result2.(*obj.List)
	if !ok {
		t.Errorf("cdr didn't return a List. Got %T", result2)
		return
	}

	elements2 := list2.ToSlice()
	if len(elements2) != 0 {
		t.Errorf("cdr of single-element list returned a list with wrong length. Got %d, want 0", len(elements2))
	}
}

// TestListFunction tests the list function
func TestListFunction(t *testing.T) {
	// Test (list 1 2 3)
	listExpr1 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "list"},
			&ast.IntegerLiteral{Value: 1},
			&ast.IntegerLiteral{Value: 2},
			&ast.IntegerLiteral{Value: 3},
		},
	}

	// Test (list) - empty list
	listExpr2 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "list"},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test case 1: (list 1 2 3)
	result1 := Eval(listExpr1, env)
	list1, ok := result1.(*obj.List)
	if !ok {
		t.Errorf("list didn't return a List. Got %T", result1)
		return
	}

	elements1 := list1.ToSlice()
	if len(elements1) != 3 {
		t.Errorf("list returned a list with wrong length. Got %d, want 3", len(elements1))
		return
	}

	// Check each element
	expectedValues := []int64{1, 2, 3}
	for i, expected := range expectedValues {
		if intVal, ok := elements1[i].(*obj.Integer); !ok || intVal.Value != expected {
			t.Errorf("Element %d is wrong. Got %v, want %d", i, elements1[i], expected)
		}
	}

	// Test case 2: (list) - empty list
	result2 := Eval(listExpr2, env)
	list2, ok := result2.(*obj.List)
	if !ok {
		t.Errorf("list didn't return a List. Got %T", result2)
		return
	}

	elements2 := list2.ToSlice()
	if len(elements2) != 0 {
		t.Errorf("(list) returned a list with wrong length. Got %d, want 0", len(elements2))
	}
}

// TestNullFunction tests the null? function
func TestNullFunction(t *testing.T) {
	// Test (null? ())
	nullExpr1 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "null?"},
			&ast.ListExpression{Elements: []ast.Expression{}},
		},
	}

	// Test (null? (list 1 2 3))
	nullExpr2 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "null?"},
			&ast.ListExpression{
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "list"},
					&ast.IntegerLiteral{Value: 1},
					&ast.IntegerLiteral{Value: 2},
					&ast.IntegerLiteral{Value: 3},
				},
			},
		},
	}

	// Test (null? 42) - non-list argument
	nullExpr3 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "null?"},
			&ast.IntegerLiteral{Value: 42},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test case 1: (null? ())
	result1 := Eval(nullExpr1, env)
	boolVal1, ok := result1.(*obj.Boolean)
	if !ok {
		t.Errorf("null? didn't return a Boolean. Got %T", result1)
		return
	}

	if !boolVal1.Value {
		t.Errorf("(null? ()) should return #t. Got #f")
	}

	// Test case 2: (null? (list 1 2 3))
	result2 := Eval(nullExpr2, env)
	boolVal2, ok := result2.(*obj.Boolean)
	if !ok {
		t.Errorf("null? didn't return a Boolean. Got %T", result2)
		return
	}

	if boolVal2.Value {
		t.Errorf("(null? (list 1 2 3)) should return #f. Got #t")
	}

	// Test case 3: (null? 42)
	result3 := Eval(nullExpr3, env)
	boolVal3, ok := result3.(*obj.Boolean)
	if !ok {
		t.Errorf("null? didn't return a Boolean. Got %T", result3)
		return
	}

	if boolVal3.Value {
		t.Errorf("(null? 42) should return #f. Got #t")
	}
}

// TestPairFunction tests the pair? function
func TestPairFunction(t *testing.T) {
	// Test (pair? (list 1 2 3))
	pairExpr1 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "pair?"},
			&ast.ListExpression{
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "list"},
					&ast.IntegerLiteral{Value: 1},
					&ast.IntegerLiteral{Value: 2},
					&ast.IntegerLiteral{Value: 3},
				},
			},
		},
	}

	// Test (pair? ())
	pairExpr2 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "pair?"},
			&ast.ListExpression{Elements: []ast.Expression{}},
		},
	}

	// Test (pair? 42) - non-list argument
	pairExpr3 := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "pair?"},
			&ast.IntegerLiteral{Value: 42},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test case 1: (pair? (list 1 2 3))
	result1 := Eval(pairExpr1, env)
	boolVal1, ok := result1.(*obj.Boolean)
	if !ok {
		t.Errorf("pair? didn't return a Boolean. Got %T", result1)
		return
	}

	if !boolVal1.Value {
		t.Errorf("(pair? (list 1 2 3)) should return #t. Got #f")
	}

	// Test case 2: (pair? ())
	result2 := Eval(pairExpr2, env)
	boolVal2, ok := result2.(*obj.Boolean)
	if !ok {
		t.Errorf("pair? didn't return a Boolean. Got %T", result2)
		return
	}

	if boolVal2.Value {
		t.Errorf("(pair? ()) should return #f. Got #t")
	}

	// Test case 3: (pair? 42)
	result3 := Eval(pairExpr3, env)
	boolVal3, ok := result3.(*obj.Boolean)
	if !ok {
		t.Errorf("pair? didn't return a Boolean. Got %T", result3)
		return
	}

	if boolVal3.Value {
		t.Errorf("(pair? 42) should return #f. Got #t")
	}
}

// TestNestedListOperations tests nested list operations
func TestNestedListOperations(t *testing.T) {
	// Test (car (cdr (list 1 2 3 4)))
	nestedExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "car"},
			&ast.ListExpression{
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "cdr"},
					&ast.ListExpression{
						Elements: []ast.Expression{
							&ast.SymbolLiteral{Value: "list"},
							&ast.IntegerLiteral{Value: 1},
							&ast.IntegerLiteral{Value: 2},
							&ast.IntegerLiteral{Value: 3},
							&ast.IntegerLiteral{Value: 4},
						},
					},
				},
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the nested expression
	result := Eval(nestedExpr, env)
	intVal, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Nested list operation didn't return an Integer. Got %T", result)
		return
	}

	// The expected result is 2, since (cdr (list 1 2 3 4)) returns (2 3 4), and (car (2 3 4)) returns 2
	if intVal.Value != 2 {
		t.Errorf("Nested list operation returned wrong value. Got %d, want 2", intVal.Value)
	}
}

// TestCdrOfDottedPair tests that (cdr (cons 1 2)) returns 2, not (2)
func TestCdrOfDottedPair(t *testing.T) {
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Create a dotted pair (cons 1 2)
	dottedPair := Cons([]obj.Object{
		&obj.Integer{Value: 1},
		&obj.Integer{Value: 2},
	}, env)

	// Apply cdr to the dotted pair
	cdrResult := Cdr([]obj.Object{dottedPair}, env)

	// The result should be the integer 2, not a list containing 2
	intResult, ok := cdrResult.(*obj.Integer)
	if !ok {
		t.Errorf("cdr of dotted pair should return an Integer, got %T", cdrResult)
		return
	}

	if intResult.Value != 2 {
		t.Errorf("cdr of dotted pair should return 2, got %d", intResult.Value)
	}
}

// TestCarOfDottedPair tests that (car (cons 1 2)) returns 1
func TestCarOfDottedPair(t *testing.T) {
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Create a dotted pair (cons 1 2)
	dottedPair := Cons([]obj.Object{
		&obj.Integer{Value: 1},
		&obj.Integer{Value: 2},
	}, env)

	// Apply car to the dotted pair
	carResult := Car([]obj.Object{dottedPair}, env)

	// The result should be the integer 1
	intResult, ok := carResult.(*obj.Integer)
	if !ok {
		t.Errorf("car of dotted pair should return an Integer, got %T", carResult)
		return
	}

	if intResult.Value != 1 {
		t.Errorf("car of dotted pair should return 1, got %d", intResult.Value)
	}
}

// TestCdrOfProperList tests that (cdr (cons 1 '(2))) returns (2), not 2
func TestCdrOfProperList(t *testing.T) {
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Create a proper list '(2)
	properList := &obj.List{Car: &obj.Integer{Value: 2}, Cdr: &obj.List{}}

	// Create (cons 1 '(2)) which should be a proper list (1 2)
	listWithCons := Cons([]obj.Object{
		&obj.Integer{Value: 1},
		properList,
	}, env)

	// Apply cdr to the result
	cdrResult := Cdr([]obj.Object{listWithCons}, env)

	// For a proper list (1 2), cdr should return (2), not 2
	// This is the correct behavior according to Scheme specification
	listResult, ok := cdrResult.(*obj.List)
	if !ok {
		t.Errorf("cdr of cons result should return a List, got %T", cdrResult)
		return
	}
	
	elements := listResult.ToSlice()
	if len(elements) != 1 {
		t.Errorf("cdr result should be a list with 1 element, got %d", len(elements))
		return
	}
	
	intResult, ok := elements[0].(*obj.Integer)
	if !ok {
		t.Errorf("first element of cdr result should be an Integer, got %T", elements[0])
		return
	}

	if intResult.Value != 2 {
		t.Errorf("first element of cdr result should be 2, got %d", intResult.Value)
	}
}
